<?php
declare(strict_types=1);

namespace SixShop\Core\Service;

use Composer\InstalledVersions;
use Composer\Json\JsonFile;
use SixShop\Core\Exception\ExceptionHandle;
use SixShop\Core\Plugin;
use SixShop\Core\Request;
use think\event\HttpRun;
use think\exception\Handle;
use think\Service;
use function SixShop\Core\extension_path;

class CoreService extends Service
{
    public static string $extensionPath;

    /**
     * @var array<string, array{name:string,version:string}>
     */
    public static array $extensionComposerMap = [];

    /* @deprecated */
    public static array $extensionNameList = [];

    private static array $normalExtensionNameList = [];

    public static function getExtensionComposerMap(): array
    {
        return self::$extensionComposerMap;
    }

    public function register(): void
    {
        $this->app->bind(Handle::class, ExceptionHandle::class);
        $this->app->bind('think\Request', Request::class);
        $this->app->bind('classLoader',
            require $this->app->getRootPath() . 'vendor/autoload.php');
        self::$extensionPath = $this->app->getRootPath() . 'extension' . DIRECTORY_SEPARATOR;
        $this->initExtensionList();
        $this->compatibleExtensionNameList();
    }

    private function initExtensionList(): void
    {
        if (!empty(self::$extensionComposerMap)) {
            return;
        }
        $runtimePath = $this->app->getRootPath() . 'runtime/';
        $reference = Plugin::getInstalledSixShopExtensions()['root']['reference'];
        $extensionComposerFile = $runtimePath . 'extension_' . $reference . '.php';
        if (file_exists($extensionComposerFile)) {
            self::$extensionComposerMap = require $extensionComposerFile;
            return;
        }
        $files = array_diff(scandir($runtimePath), ['.', '..']);
        foreach ($files as $file) {
            if (str_starts_with($file, 'extension_') && str_ends_with($file, '.php')) {
                unlink($runtimePath . $file);
            }
        }
        foreach (InstalledVersions::getInstalledPackagesByType(Plugin::EXTENSION_TYPE) as $item) {
            $installPath = InstalledVersions::getInstallPath($item);
            $composerJson = new JsonFile($installPath . '/composer.json');
            $composer = $composerJson->read();
            $extensionID = $composer['extra']['sixshop']['id'] ?? $composer['name'];
            self::$extensionComposerMap[$extensionID] = $composer;
        }
        $header = '// This file is automatically generated at:' . date('Y-m-d H:i:s') . PHP_EOL . 'declare (strict_types = 1);' . PHP_EOL;
        $content = '<?php ' . PHP_EOL . $header . "return " . var_export(self::$extensionComposerMap, true) . ';';
        file_put_contents($extensionComposerFile, $content);
    }

    /* @deprecated */
    private function compatibleExtensionNameList(): void
    {
        if (empty(self::$extensionNameList)) {
            self::$extensionNameList = array_keys(self::$extensionComposerMap);
            $normalFile = $this->app->getRootPath() . 'runtime/module_name_list_normal.php';
            if (file_exists($normalFile)) {
                $normalExtensionList = require $normalFile;
                foreach ($normalExtensionList as $item) {
                    if (array_key_exists($item, self::$extensionComposerMap)) {
                        continue;
                    }
                    if (is_dir(extension_path($item) . 'src')) {
                        self::$extensionNameList[] = $item;
                        self::$normalExtensionNameList[] = $item;
                    }
                }
            }
        }
    }

    public function boot(): void
    {
        $autoloadService = $this->app->make(AutoloadService::class);
        $invalidExtensionIDs = $autoloadService->load(self::$extensionComposerMap, self::$normalExtensionNameList);
        self::$extensionNameList = array_diff(self::$extensionNameList, $invalidExtensionIDs);
        $this->app->get('extension.system')->boot();
        foreach (self::$extensionComposerMap + self::$normalExtensionNameList as $moduleName => $_) {
            if (in_array($moduleName, $invalidExtensionIDs)) {
                continue;
            }
            $extension = $autoloadService->getExtension($moduleName);
            if (!$extension->available()) {
                continue;
            }
            $extension->boot();
            $this->app->event->trigger('extension.boot', $extension);
        }
        $this->app->make(HookAttributeService::class)->init();
        $this->app->event->trigger('hook_init', $this->app);
        $this->app->event->listen(HttpRun::class, function () {
            $this->registerRoutes($this->app->make(RegisterRouteService::class)->init());
        });

        $this->app->make(CommandService::class)->init(function ($commands) {
            $this->commands($commands);
        });
    }
}